package com.apes.fn.server.productReturn;
import com.apes.fn.server.productReceive.ProductReceiveRepository;
import com.apes.fn.server.servicePlan.model.ServicePlan;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.server.productReceive.ProductReceiveItem;
import com.apes.fn.server.productReceive.ProductReceiveItemRepository;
import com.apes.fn.server.productReceive.ProductReceiveService;
import com.apes.fn.server.servicePlan.model.ServicePlanItem;
import com.apes.fn.server.servicePlan.repository.ServicePlanDao;
import com.apes.fn.server.servicePlan.repository.ServicePlanItemRepository;
import com.apes.fn.server.servicePlan.service.ServicePlanceService;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.logger.ApesLoggerService;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.jpa.spec.model.Filter;
import com.apes.framework.jpa.spec.model.Group;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.service.ProductAccountService;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Product;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.*;
import java.util.*;
import java.util.stream.Collectors;

@Service("productReturnService")
public class ProductReturnService extends DomainService {

    @Autowired
    ProductReturnRepository productReturnRepository;
    @Autowired
    ProductAccountService productAccountService;
    @Autowired
    ProductReceiveItemRepository productReceiveItemRepository;
    @Autowired
    ProductReceiveRepository productReceiveRepository;
    @Autowired
    ServicePlanItemRepository servicePlanItemRepository;
    @Autowired
    private ApesLoggerService apesLoggerService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private ServicePlanceService servicePlanceService;
    /**
     * 创建
     *
     * @param request ProductReturn
     * @return ProductReturn
     */
    public ProductReturn create(SimpleRequest request) {
        ProductReturn productReturn = request.getO(ProductReturn.class);
        productReturn.setState("create");
        clearZero(productReturn); // 清除归还数为零的行
        return save(productReturn);
    }

    public ProductReturn save(ProductReturn productReturn) {
        return productReturnRepository.saveAndFlush(productReturn);
    }

    /**
     * 自动归还
     * 入参:
     * 明细列表 :List
     *  方案明细单号 : id
     *  归还商品 : productId
     *  方案编码 : planId 用于查询质检单据下的可归还商品
     *  车牌号码 : carLicense
     *  库存位置 : location
     *  归还数量 : returnNumber
     *
     * @param request
     */
    public List<ProductReturn> autoReturn(SimpleRequest request){
        JSONArray returnProduct = request.getJA();
        PartyRole returnPerson = request.getPerson();
        List<ProductReturn> productReturns = new ArrayList<>();
        //按仓库维度划分明细
        Map<String, List<Object>> returnProductLocationMap = returnProduct.stream().collect(Collectors.groupingBy(o -> ((JSONObject) o).getString("location")));
        for (String locationId : returnProductLocationMap.keySet()) {
            List<Object> currentLocationReturn = returnProductLocationMap.get(locationId);
            //根据仓库查询可归还明细
            List<Map> returnable = this.invoke("stock.productReceive.findAllReturnable", MapUtil.mapper("id", locationId));
            //获取需要归还的明细
            JSONArray returnableArray = getReturnOderItemArray(locationId, currentLocationReturn, returnable);
            //创建并审核商品领用单 并且回写方案日志
            ProductReturn productReturnOrder = getProductReturnOrderByAutoReturn(request, returnProduct, returnPerson, returnableArray);

            productReturns.add(productReturnOrder);
        }
        //回写方案终止日志
        rewriteServicePlanLogger(returnProduct, productReturns);

        return productReturns;


    }

    /**
     * 回写方案终止日志
     * @param jsonArray
     * @param productReturns
     */
    private void rewriteServicePlanLogger(JSONArray jsonArray, List<ProductReturn> productReturns) {
        StringBuffer sf = new StringBuffer();

        for (int i = 0; i < productReturns.size(); i++) {
            if (i+1==productReturns.size()){
                sf.append(productReturns.get(i).getId());
            }else {
                sf.append(productReturns.get(i).getId()+",");
            }
        }
        ServicePlan servicePlan = servicePlanItemRepository.findOne(((JSONObject) jsonArray.get(0)).getString("id")).getServicePlan();
        servicePlanceService.addServicePlanLogger(servicePlan,"配件归还","触发配件归还,单号:"+sf.toString());
    }

    /**
     * 获取领用单明细 数组
     * @param locationId 仓库编码
     * @param currentLocationReturn 同一仓库下的归还商品
     * @param canReturnable 可归还的商品
     * @return
     */
    private JSONArray getReturnOderItemArray(String locationId, List<Object> currentLocationReturn, List<Map> canReturnable) {
        JSONArray returnableArray = new JSONArray();

        for (Object o : currentLocationReturn) {
            JSONObject returnProduct = (JSONObject) o;
            JSONObject productReceiveItems = new JSONObject();
            List<Map> returnable = canReturnable;
            if ("qualityTest".equals(returnProduct.getString("state"))){
                //检查当前单据下的可归还商品(如果是质检状态的话) 需要当前单据属于质检状态
                returnable = this.invoke("stock.productReceive.findAllReturnableByPlanId", MapUtil.mapper("locationId", locationId, "planId", (String) returnProduct.get("planId")));
            }
            //遍历领用单明细查找每个可归还数
            findReturnQty(returnable, returnProduct, productReceiveItems);

            if (productReceiveItems.isEmpty())throw new RuntimeException("该商品["+returnProduct.getString("productId")+"]在["+locationId+"]仓库下不存在可归还明细");

            returnProduct.put("productReceiveItem",productReceiveItems);
            returnableArray.add(returnProduct);
        }
        return returnableArray;
    }

    private void findReturnQty(List<Map> returnable, JSONObject item, JSONObject productReceiveItems) {
        for (Map map : returnable) {
            Product product = (Product) map.get("product");
            String id = product.getId();
            if (item.getString("productId").equals(id) && item.getString("carLicense").equals(map.get("carLicense"))) {
                productReceiveItems.put((String) ((Map) map.get("productReceiveItem")).get("id"), map.get("qtyReturnable"));
            }
        }
    }

    private ProductReturn getProductReturnOrderByAutoReturn(SimpleRequest request, JSONArray jsonArray, PartyRole returnPerson, JSONArray returnableArray) {
        //创建归还单
        ProductReturn productReturnOrder = createProductReturnOrder(returnableArray, returnPerson);
        //添加归还单审核日志
        addProductReturnLogger(productReturnOrder,"创建",null);
        //设置审核人
        productReturnOrder.setApproveId(returnPerson);
        //审核归还单
        productReturnOrder = approveOrder(productReturnOrder);
        //添加归还单审核日志
        addProductReturnLogger(productReturnOrder,"审核",null);
        return productReturnOrder;
    }

    public void addProductReturnLogger(ProductReturn productReturnOrder,String operate,String remark){
        this.invoke("ir.logger.add",MapUtil.mapper(
                "businessKey",productReturnOrder.getInnerBusinessKey(),
                "businessType",productReturnOrder.getInnerBusinessType(),
                "operate",operate,
                "oaFlag",false,
                "remark",remark

        ));
    }

    /**
     * 创建归还单
     * @param jsonArray
     * @param person
     * @return
     */
    private ProductReturn createProductReturnOrder(JSONArray jsonArray,PartyRole person) {
        JSONObject jsonObject = (JSONObject) jsonArray.get(0);
        ServicePlanItem servicePlanItem = servicePlanItemRepository.findOne(jsonObject.getString("id"));
        ServicePlan servicePlan = servicePlanItem.getServicePlan();

        ProductReturn productReturn = new ProductReturn();
        productReturn.setSourceBusinessType(servicePlan.getClass().getSimpleName());
        productReturn.setSourceBusinessTypeName(servicePlan.getBusinessTypeName());
        productReturn.setLocation(servicePlanItem.getLocation());
        productReturn.setCompany(servicePlanItem.getLocation().getCompany());
        productReturn.setRemark("方案:"+servicePlan.getId()+" 终止自动归还");
        productReturn.setState("create");
        productReturn.setCreator(person.getId());
        ArrayList<ProductReturnItem> productReturnItems = new ArrayList<>();

        productReturn.setProductReturnItems(createProductReturnItems(productReturnItems,productReturn,jsonArray));
        return productReturnRepository.saveAndFlush(productReturn);

    }



    /**
     * 递归创建归还明细
     *
     * @param productReturnItems
     * @param productReturn
     * @param items
     * @return
     */
    private List<ProductReturnItem> createProductReturnItems(ArrayList<ProductReturnItem> productReturnItems, ProductReturn productReturn, JSONArray items) {
        JSONArray newItems = new JSONArray();
        items.forEach(item ->{
            JSONObject serviceItems = (JSONObject) item;
            Integer returnNumber = serviceItems.getInteger("returnNumber");
            if (returnNumber<0)throw new RuntimeException("归还数小于零");
            ServicePlanItem servicePlanItem = servicePlanItemRepository.findOne(serviceItems.getString("id"));
            ProductReceiveItem receiveItem = matchProductReceiveItem(serviceItems, returnNumber);

            ProductReturnItem productReturnItem = new ProductReturnItem();
            productReturnItem.setProductReceiveItem(receiveItem);
            productReturnItem.setCompany(productReturn.getCompany());
            productReturnItem.setCarLicense(receiveItem.queryCarLicense());
            productReturnItem.setProduct(servicePlanItem.getProduct());
            productReturnItem.setUom(servicePlanItem.getUom());
            productReturnItem.setQtyReturn(receiveItem.getQtyReceive() - receiveItem.getQtyReturned()>=returnNumber?returnNumber:receiveItem.getQtyReceive() - receiveItem.getQtyReturned());
            productReturnItem.setRemark("方案终止自动归还");
            productReturnItems.add(productReturnItem);
            if (productReturnItem.getQtyReturn()!=returnNumber){
                serviceItems.put("returnNumber",returnNumber-productReturnItem.getQtyReturn());
                newItems.add(serviceItems);
            }

        });
        if (productReturnItems==null)throw new RuntimeException("缺少归还商品");
        if (newItems.size()==0)return productReturnItems;
        return createProductReturnItems(productReturnItems,productReturn, newItems);
    }

    /**
     * 匹配合适的领用单
     * @param serviceItems
     * @param returnNumber
     * @return
     */
    private ProductReceiveItem matchProductReceiveItem(JSONObject serviceItems,Integer returnNumber){
        JSONObject productReceiveItem = serviceItems.getJSONObject("productReceiveItem");
        ProductReceiveItem receiveItem = new ProductReceiveItem();
        Iterator<Map.Entry<String, Object>> iterator = productReceiveItem.entrySet().iterator();
        boolean flag = true;
        //循环匹配足够归还数量的领用单没有则取最后一张
        while (flag) {
            Map.Entry<String, Object> next = iterator.next();
            receiveItem = productReceiveItemRepository.findOne(next.getKey());
            if (serviceItems.getString("id").equals(receiveItem.getServicePlanItem().getId())||!iterator.hasNext()){
                flag = false;
            }
        }
        //删除当前领用单
        productReceiveItem.remove(receiveItem.getId());
        double v = receiveItem.getQtyReceive() - receiveItem.getQtyReturned();
        double remain = returnNumber - v;
        //判断是否全部归还 否则继续下轮归还
        if (remain>0)serviceItems.put("productReceiveItem",productReceiveItem);
        return receiveItem;
    }



    /**
     * 生成多张商品归还单并且审核
     *
     * @param request List<ProductReturn>
     * @return List<ProductReturn>
     */
    public List<ProductReturn> createAndApproveProductReturns(SimpleRequest request) {
        List<ProductReturn> list = request.getA(ProductReturn.class);
        list.stream().forEach(item -> approveOrder(item));
        return list;
    }

    /**
     * 功能: 修改
     * @param request
     * @return
     */
    public ProductReturn update(SimpleRequest request) {
        isVaild(request, productReturnRepository);
        ProductReturn productReturn = request.getO(ProductReturn.class);
        clearZero(productReturn); // 清除归还数为零的行
        return productReturnRepository.saveAndFlush(productReturn);
    }

    /**
     * 功能：删除订单
     */
    public ProductReturn delete(SimpleRequest request) {
        isVaild(request, productReturnRepository);
        ProductReturn productReturn = request.getO(ProductReturn.class);

        productReturn.setState("delete");
        productReturn.setDeleteId(getPerson(request));
        productReturn.setDeleteDate(new Date());
        return productReturnRepository.saveAndFlush(productReturn);
    }

    /**
     * 功能：审批订单
     *
     * @param request
     * @return
     */
    public ProductReturn approve(SimpleRequest request) {
        ProductReturn productReturn = (ProductReturn)isVaild(request, productReturnRepository);
        productReturn.setApproveId(getPerson(request));
        return approveOrder(productReturn);
    }


    private ProductReturn approveOrder(ProductReturn productReturn) {
        if (productReturn.getSource() != null) {
            // 来源单据默认为yyadmin
            PartyRole partyRole = SpringManager.getBean(PartyRoleRepository.class).findOne("yyadmin");
            productReturn.setCreateDate(new Date());
            productReturn.setCreator(partyRole.getId());
            productReturn.setApproveId(partyRole);
        }
        productReturn.setApproveDate(new Date());
        productReturn.setState("approve");

        productReturn = productReturnRepository.saveAndFlush(productReturn);

        //写日志
        addApproveLogger(productReturn);

        // 还可用数 还保管数 还领用在途 冻结数
        updateStock(productReturn, StringUtils.isEmpty(productReturn.getRemark()) ? "配件归还单审核" : productReturn.getRemark());

        // 写已归还数量，还服务方案领用数量
//        setQtyReturned(productReturn);

        //回写领用数量(领用单)
        SpringManager.getBean(ProductReceiveService.class).rewriteProductReceiveByProductReceive(productReturn);

        // 回写领用数量(方案)   零售单归还时不写 自动归还时不写(写了会报乐观锁)
        if ( productReturn.isScanSign() || (StringUtils.isEmpty(productReturn.getSource())&&(productReturn.getRemark()==null||!productReturn.getRemark().contains("自动归还")))) {
            servicePlanceService.rewriteServicePlanByProductReturn(productReturn);
            saveLog(productReturn);
            //做一个检测，检查方案的领用数量和领用单的领用数量是否一致，不一致报错。解决线上bug
            checkPlanAndReceive(productReturn);
        }

        return productReturn;
    }

    private void addApproveLogger(ProductReturn productReturn) {
        if (productReturn.getSource() != null) {
            // 增加日志
            apesLoggerService.sendSynEvent("event:ir.logger.create", MapUtil.mapper("businessType", "ProductReturn",
                    "businessKey", productReturn.getId(), "operate", "保存", "workPostName",null, "operator_id", 1));
            apesLoggerService.sendSynEvent("event:ir.logger.create", MapUtil.mapper("businessType", "ProductReturn",
                    "businessKey", productReturn.getId(), "operate", " 商品归还单审核", "workPostName",null, "operator_id", 1));
        }
        //写来源方案日志
        if (productReturn.getRemark()==null||!(productReturn.getRemark()!=null&&productReturn.getRemark().contains("终止自动归还"))){
            String s = "配件归还";
            if ("扫码端归还".equals(productReturn.getRemark())){
                s = "扫码归还";
            }
            String returnRemark = s;
            productReturn.getProductReturnItems().stream()
                    .map(productReturnItem -> productReturnItem.getProductReceiveItem().getServicePlanItem().getServicePlan())
                    .distinct()
                    .forEach(servicePlan -> servicePlanceService.addServicePlanLogger(servicePlan, returnRemark,productReturn.getId()));
        }
    }

    /**
     * 临时做一个领用检测，如果回写方案不正确，不给与通过
     * @param productReturn
     */
    private void checkPlanAndReceive(ProductReturn productReturn) {
        JSONObject param = new JSONObject();
        param.put("ID",productReturn.getId());
        JSONArray arrayOne =SpringManager.getBean(ServicePlanDao.class).findForJudgeReturnOne(param);

        JSONArray arrayResult = arrayOne.stream().map(item ->{
            JSONObject itemJson = (JSONObject)item;
            return itemJson.getString("SERVICE_PLAN_ID");
        }).collect(Collectors.toCollection(JSONArray::new));

        JSONArray arrayTwo =SpringManager.getBean(ServicePlanDao.class).findForJudgeReturnTwo(arrayResult);

        if(arrayTwo.size() != 0) throw new RuntimeException("归还有误，回写方案数量有误,方案号：" + arrayTwo.getJSONObject(0).getString("SERVICE_PLAN_ID") + "，商品编码：" + arrayTwo.getJSONObject(0).getString("PRODUCT_ID"));
    }

    private void saveLog(ProductReturn productReturn) {
        Map map = MapUtil.mapped(productReturn);
        Document document = new Document(map);
        document.put("indexId", productReturn.getId());
        if ( productReturn.isScanSign() || StringUtils.isEmpty(productReturn.getSource())) {
            document.put("processed", true);
        }
        mongoTemplate.getCollection("productReturn").insertOne(document);
    }


    /***
     * 写已归还数量
     */
    private void setQtyReturned(ProductReturn productReturn) {
        productReturn.getProductReturnItems().stream().forEach(productReturnItem -> {
            ProductReceiveItem productReceiveItem = productReturnItem.getProductReceiveItem();
            productReceiveItem.setQtyReturned(productReceiveItem.getQtyReturned() + productReturnItem.getQtyReturn());

            ServicePlanItem servicePlanItem = productReceiveItem.getServicePlanItem();
            servicePlanItem.addQtyReceive(-productReturnItem.getQtyReturn());
            servicePlanItemRepository.saveAndFlush(servicePlanItem);
            productReceiveItemRepository.saveAndFlush(productReceiveItem);
        });
    }


    /***
     * 验证数据合法性
     */
    private Model isVaild(SimpleRequest request, CustomRepository repository){
        Model oldModel = (Model)  repository.findOne((String) request.get("id"));
        if (oldModel == null) throw new RuntimeException("订单【" + request.get("id") + "】在系统中不存在。");
        if (oldModel.getVersion() != (int) request.get("version")) throw new RuntimeException("记录已更新或已删除，请重新提交！");
        return oldModel;
    }

    /**
     * 更新库存账
     * 可用数、保管数、冻结数、领用在途
     *
     * @param productReturn 归还单
     * @param remarks       备注
     */
    private void updateStock(ProductReturn productReturn, String remarks) {

        productReturn.getProductReturnItems().stream().forEach(
                productReturnItem -> {

                    /* 冻结数逻辑如下：
                    *  1.方案确认时增加（+）
                    *  2.领用单审核时减少（-）  此时就为0
                    *  3.归还时增加（+）
                    *  4.方案上面去终止归还的部分（-） 此时为0
                    * 零售单制单生成时就不回写冻结数
                    */
                    if (StringUtils.isEmpty(productReturn.getSource())) {
                        // 冻结数
                        productAccountService.setProductFrozen(productReturnItem.getProduct(),
                                productReturn.getLocation(), productReturnItem.getUom(), productReturnItem.getQtyReturn(),
                                productReturn.getInnerBusinessType(), productReturn.getId(), productReturnItem.getId(), remarks, productReturn.getCompany(),
                                productReturnItem.getProductReceiveItem().getServicePlanItem().getServicePlan().getInnerBusinessType(),
                                productReturnItem.getProductReceiveItem().getServicePlanItem().getServicePlan().getInnerBusinessKey(),
                                productReturnItem.getProductReceiveItem().getServicePlanItem().getInnerBusinessKey());
                    }

                    // 可用数
                    productAccountService.setProductAvailable(productReturnItem.getProduct(),
                            productReturn.getLocation(), productReturnItem.getUom(), productReturnItem.getQtyReturn(),
                            productReturn.getInnerBusinessType(), productReturn.getId(), productReturnItem.getId(), remarks, productReturn.getCompany());

                    // 保管数
                    productAccountService.setProductCustody(productReturnItem.getProduct(),
                            productReturn.getLocation(), productReturnItem.getUom(), productReturnItem.getQtyReturn(),
                            productReturn.getInnerBusinessType(), productReturn.getId(), productReturnItem.getId(), remarks, productReturn.getCompany());

                    // 领用在途
                    productAccountService.setProductLend(productReturnItem.getProduct(),
                            productReturn.getLocation(), productReturnItem.getProductReceiveItem().getProductReceive().getReceiver(),
                            productReturnItem.getUom(), -1 * productReturnItem.getQtyReturn(), "lendReturn", productReturn.getCompany());

                });
    }

    /***
     * 清除归还数为零的行
     */
    private void clearZero(ProductReturn productReturn){
        List<ProductReturnItem> productReturnItems = productReturn.getProductReturnItems();
        Iterator<ProductReturnItem> iterator = productReturnItems.iterator();
        // 筛选数量为零的行
        while(iterator.hasNext()){
            ProductReturnItem item = iterator.next();
            if(item.getQtyReturn() == 0) {
                iterator.remove();
            }
        }
        if (productReturnItems.size() == 0) throw new RuntimeException("明细归还数量全为零或者明细为空");
    }

    /***
     *  通过id查询方案号，归还单号，领用单号
     * @return
     */
    public Page<ProductReturn> findProductReturnByIds(SimpleRequest request){
        Group group = findAllCore(request);
        return productReturnRepository.findAll(group.build(true), JpaDsl.getPageable(request));
    }

    /***
     * 抽取的公共逻辑
     */
    private Group findAllCore(SimpleRequest request) {
        Group group = JpaDsl.getGroup(request);

        List<Filter> filters = group.getFilters();
        Filter filterCarLence = filters.stream().filter( item -> item.getField().equals("carLence")).findFirst().orElse(null);
        // 筛选出对应的id， 用于匹配 方案号，归还单号，领用单号
        Filter filter = filters.stream().filter( item -> item.getField().equals("id")).findFirst().orElse(null);

        if( filter != null ) {
            filters.remove(filter); // 移除当前选项
            group.addGroup(createIdGroup(filter));
        }

//        if ( filterCarLence != null) {
//            filters.remove(filterCarLence);
//            Group carLenceGroup = new Group();
//            carLenceGroup.setOperator("AND");
//            carLenceGroup.addFilters(JpaDsl.toFilter(
//                    "productReturnItems.productReceiveItem.servicePlanItem.customerExpand.carLence","EQ", filterCarLence.getValue()));
//            group.addGroup(createIdGroup(filter));
//        }

        return group;
    }


    /***
     * 构建订单 Group
     */
    private Group createIdGroup(Filter filter) {
        Group idGroup = new Group();
        idGroup.setOperator("or");
        idGroup.addFilters(
                JpaDsl.toFilter(
                        "productReturnItems.productReceiveItem.productReceive.id","EQ", filter.getValue(),
                        "productReturnItems.productReceiveItem.productReceive.source", "EQ", filter.getValue(),
                        "id", "EQ", filter.getValue()
                )
        );
        return idGroup;
    }

}
